﻿#region Copyright ©2008-2009, Technische Universitaet Muenchen
// ====================================================================================================
//
// Last Changed by       $Author: then $
// Last Changed Date     $LastChangedDate: 2009-05-16 14:58:56 +0200 (Sat, 16 May 2009) $
// Last Changed Revision $Rev: 144 $
//
// ====================================================================================================
#endregion

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
using Tum.CollabXT.TFS.Resources;

namespace Tum.CollabXT.TFS
{
    public class WorkItems : IProcessor
    {
        private List<string> IterationPathList = new List<string>();

        public void Process(IProcessProvider processProvider, TFSToolProvider toolProvider)
        {
            string workItemTemplateFileName = toolProvider.OutputPath + "/WorkItem Tracking/workitems.xml";

            #region Load template
            XmlDocument workItemDoc = new XmlDocument();
            toolProvider.ProcessLog.AddEntry(Language.WorkItems_Loading);
            try
            {
                workItemDoc.Load(workItemTemplateFileName);
            }
            catch
            {
                toolProvider.ProcessLog.AddEntryAndThrowException(string.Format(Language.Error_LoadingFailed, workItemTemplateFileName));
            }
            #endregion

            toolProvider.ProcessLog.AddEntry(Language.WorkItems_SelectDefaultNode);
            var defaultWorkItemsNode = workItemDoc.SelectSingleNode("/tasks/task[@id=\"WIs\"]/taskXml/WORKITEMS");
            if (defaultWorkItemsNode == null)
                toolProvider.ProcessLog.AddEntryAndThrowException(Language.Error_NotFound_InvalidTemplate);

            #region Write milestones
            //Create hashset for decision gate types
            HashSet<string> decisionGateTypes = new HashSet<string>();

            //Insert some comment
            defaultWorkItemsNode.AppendChild(workItemDoc.CreateComment("Decision gates"));

            //Write
            foreach (IMilestone curMilestone in processProvider.Milestones)
            {
                try
                {
                    toolProvider.ProcessLog.AddEntry(string.Format(Language.WorkItems_WritingDecisionGate, curMilestone.Name));

                    //Create default work item node
                    XmlNode newDefWorkItemNode = workItemDoc.CreateNode("element", "WI", "");

                    //Type attribute
                    XmlAttribute typeAttribute = workItemDoc.CreateAttribute("type");
                    typeAttribute.Value = "Entscheidungspunkt"; //dec. gate
                    newDefWorkItemNode.Attributes.Append(typeAttribute);
                    //--------------

                    AddFieldToXmlNode(newDefWorkItemNode, "System.Title", Helper.PreapareStringForTFS(curMilestone.Name, Helper.TFS_MaxWorkItemTitleLen));
                    AddFieldToXmlNode(newDefWorkItemNode, "System.Description", Helper.PreapareStringForTFS(curMilestone.Description, Helper.TFS_MaxWorkItemTextLen));
                    AddFieldToXmlNode(newDefWorkItemNode, "System.State", toolProvider.LanguageDictionary["State_Planned"]);

                    #region Write iteration path
                    object devPhase;
                    if (curMilestone.ExtendedData.TryGetValue("DevPhase", out devPhase))
                    {
                        if (devPhase is int)
                        {
                            string iterationPathStr = string.Format("{0:D2} ", (int)devPhase);
                            switch ((int)devPhase)
                            {
                                case 0:
                                    iterationPathStr += toolProvider.LanguageDictionary["IterationPath_Initiation"];
                                    break;

                                case 99:
                                    iterationPathStr += toolProvider.LanguageDictionary["IterationPath_Closing"];
                                    break;

                                default:
                                    iterationPathStr += toolProvider.LanguageDictionary["IterationPath_Execution"];
                                    break;
                            }

                            object devIteration;
                            if (curMilestone.ExtendedData.TryGetValue("DevIteration", out devIteration))
                            {
                                if (devIteration is int)
                                   iterationPathStr += string.Format("\\" + toolProvider.LanguageDictionary["IterationPath_Iteration"] + " {0:D2}", (int)devIteration);
                            }

                            IterationPathList.Add(iterationPathStr);

                            iterationPathStr = "$$PROJECTNAME$$\\" + iterationPathStr;
                            curMilestone.ExtendedData["__TFS_IterationPath"] = iterationPathStr;
                            AddFieldToXmlNode(newDefWorkItemNode, "System.IterationPath", iterationPathStr);
                        }
                    }
                    #endregion

                    AddFieldToXmlNode(newDefWorkItemNode, "VMXT.id", curMilestone.Id);

                    //Decision gate type
                    string curDGType = Helper.PreapareStringForTFS(curMilestone.TypeName, Helper.TFS_MaxWorkItemTitleLen);
                    AddFieldToXmlNode(newDefWorkItemNode, "VMXT.Entscheidungspunkttyp", curDGType);
                    decisionGateTypes.Add(curDGType); //Add to type list

                    //Finish date
                    string finishDateStr = curMilestone.ScheduledDate.ToString("dd/MM/yy", CultureInfo.InvariantCulture);
                    curMilestone.ExtendedData["__TFS_FinishDate"] = finishDateStr;
                    AddFieldToXmlNode(newDefWorkItemNode, "Microsoft.VSTS.Scheduling.FinishDate", finishDateStr);

                    defaultWorkItemsNode.AppendChild(newDefWorkItemNode);
                }
                catch (Exception e)
                {
                    toolProvider.ProcessLog.AddEntryAndThrowException(string.Format(Language.Error_Unexpected, e.Message, e.StackTrace));
                }
            }
            #endregion

            WriteDecisionGateTypeDefinitionFile(decisionGateTypes, toolProvider.OutputPath, toolProvider.ProcessLog);

            #region Write activities
            defaultWorkItemsNode.AppendChild(workItemDoc.CreateComment("Activities"));

            foreach (IProduct curProduct in processProvider.Products)
            {
                //Check whether product is initial
                if (!curProduct.IsInitial)
                    continue;

                IActivity[] belongingActivities = processProvider.GetActivitiesByProductID(curProduct.Id);
                IMilestone[] belongingMilestones = processProvider.GetMilestonesByProductID(curProduct.Id);

                foreach (IMilestone curMilestone in belongingMilestones)
                {
                    foreach (IActivity curActivity in belongingActivities)
                    {
                        try
                        {
                            toolProvider.ProcessLog.AddEntry(string.Format(Language.WorkItems_WritingActivity, curActivity.Name, curMilestone.Name));

                            //Create default work item node
                            XmlNode newDefWorkItemNode = workItemDoc.CreateNode("element", "WI", "");

                            //Type attribute
                            XmlAttribute typeAttribute = workItemDoc.CreateAttribute("type");
                            typeAttribute.Value = "Aktivität"; //activity
                            newDefWorkItemNode.Attributes.Append(typeAttribute);
                            //--------------

                            AddFieldToXmlNode(newDefWorkItemNode, "System.Title", Helper.PreapareStringForTFS(curActivity.Name + " (" + curMilestone.Name + ")", Helper.TFS_MaxWorkItemTitleLen));
                            AddFieldToXmlNode(newDefWorkItemNode, "System.Description", Helper.PreapareStringForTFS(curActivity.Description, Helper.TFS_MaxWorkItemTextLen));
                            AddFieldToXmlNode(newDefWorkItemNode, "System.State", toolProvider.LanguageDictionary["State_Planned"]);
                            AddFieldToXmlNode(newDefWorkItemNode, "VMXT.id", curActivity.Id);

                            AddFieldToXmlNode(newDefWorkItemNode, "VMXT.EPRef", curMilestone.Id);
                            AddFieldToXmlNode(newDefWorkItemNode, "VMXT.PRef", curProduct.Id);

                            #region IterationPath / FinishDate
                            try
                            {
                                //Write FinishDate/IterationPath and save them into the activity
                                string finishDate = curMilestone.ExtendedData["__TFS_FinishDate"] as string;
                                AddFieldToXmlNode(newDefWorkItemNode, "Microsoft.VSTS.Scheduling.FinishDate", finishDate);
                                curActivity.ExtendedData["__TFS_FinishDate"] = finishDate;

                                string iterationPath = curMilestone.ExtendedData["__TFS_IterationPath"] as string;
                                AddFieldToXmlNode(newDefWorkItemNode, "System.IterationPath", iterationPath);
                                curActivity.ExtendedData["__TFS_IterationPath"] = iterationPath;
                            }
                            catch { }
                            #endregion

                            defaultWorkItemsNode.AppendChild(newDefWorkItemNode);
                        }
                        catch (Exception e)
                        {
                            toolProvider.ProcessLog.AddEntryAndThrowException("Failed. Exception: " + e.Message);
                        }
                    }
                }
            }
            #endregion

            #region Write tasks
            defaultWorkItemsNode.AppendChild(workItemDoc.CreateComment("Tasks"));

            foreach (ITask curTask in processProvider.Tasks)
            {
                try
                {
                    //Find product belonging to this task
                    IProduct belondingProduct = null;
                    foreach (Dependency curDependency in processProvider.Dependencies)
                    {
                        if (curDependency is ProductActivityDependency)
                        {
                            ProductActivityDependency productDependency = curDependency as ProductActivityDependency;
                            if (productDependency.ActivityId.Equals(curTask.ActivityId, StringComparison.OrdinalIgnoreCase))
                            {
                                //Find product
                                foreach (IProduct curProduct in processProvider.Products)
                                {
                                    if (curProduct.Id.Equals(productDependency.ProductId, StringComparison.OrdinalIgnoreCase))
                                    {
                                        belondingProduct = curProduct;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if (belondingProduct == null)
                        continue;
                    //--------------------------------------

                    //Only write initial tasks
                    if (!belondingProduct.IsInitial)
                        continue;


                    toolProvider.ProcessLog.AddEntry(string.Format(Language.WorkItems_WritingTask, curTask.Name));

                    //Create default work item node
                    XmlNode newDefWorkItemNode = workItemDoc.CreateNode("element", "WI", "");

                    //Type attribute
                    XmlAttribute typeAttribute = workItemDoc.CreateAttribute("type");
                    typeAttribute.Value = "Arbeitsauftrag"; //task
                    newDefWorkItemNode.Attributes.Append(typeAttribute);
                    //--------------

                    AddFieldToXmlNode(newDefWorkItemNode, "System.Title", Helper.PreapareStringForTFS(curTask.Name, Helper.TFS_MaxWorkItemTitleLen));
                    AddFieldToXmlNode(newDefWorkItemNode, "System.Description", Helper.PreapareStringForTFS(curTask.Description, Helper.TFS_MaxWorkItemTextLen));

                    AddFieldToXmlNode(newDefWorkItemNode, "VMXT.ARef", curTask.ActivityId);

                    #region Discipline
                    try
                    {
                        //Find discipline
                        foreach (IDiscipline curDiscipline in processProvider.Disciplines)
                        {
                            if (curDiscipline.Id.Equals(belondingProduct.DisciplineId, StringComparison.OrdinalIgnoreCase))
                            {
                                AddFieldToXmlNode(newDefWorkItemNode, "VMXT.Discipline", curDiscipline.Name);
                                break;
                            }
                        }
                    }
                    catch { } 
                    #endregion

                    #region IterationPath / FinishDate
                    try
                    {
                        IActivity belongingActivity = null;
                        foreach (var curActivity in processProvider.Activities)
                        {
                            if (curActivity.Id.Equals(curTask.ActivityId, StringComparison.OrdinalIgnoreCase))
                            {
                                belongingActivity = curActivity;
                                break;
                            }
                        }

                        if (belongingActivity != null)
                        {
                            AddFieldToXmlNode(newDefWorkItemNode, "Microsoft.VSTS.Scheduling.FinishDate", belongingActivity.ExtendedData["__TFS_FinishDate"] as string);
                            AddFieldToXmlNode(newDefWorkItemNode, "System.IterationPath", belongingActivity.ExtendedData["__TFS_IterationPath"] as string);
                        }
                    }
                    catch(KeyNotFoundException)
                    {
                        toolProvider.ProcessLog.AddEntry(string.Format(Language.Warning_TaskWithoutMilestone, curTask.Name), LogEntryType.Warning);
                    } 
                    #endregion

                    defaultWorkItemsNode.AppendChild(newDefWorkItemNode);
                }
                catch (Exception e)
                {
                    toolProvider.ProcessLog.AddEntryAndThrowException(string.Format(Language.Error_Unexpected, e.Message, e.StackTrace));
                }
            }
            #endregion

            WriteTaskTypeDefinitionFile(processProvider.Disciplines, toolProvider.OutputPath, toolProvider.ProcessLog);

            #region Write initial products
            //Insert some comment
            defaultWorkItemsNode.AppendChild(workItemDoc.CreateComment("Initial products"));

            //Write
            foreach (IProduct curProduct in processProvider.Products)
            {
                if (curProduct.IsInitial)
                {
                    try
                    {
                        toolProvider.ProcessLog.AddEntry(string.Format(Language.WorkItems_WritingProduct, curProduct.Name));

                        //Create default work item node
                        XmlNode newDefWorkItemNode = workItemDoc.CreateNode("element", "WI", "");

                        //Type attribute
                        XmlAttribute typeAttribute = workItemDoc.CreateAttribute("type");
                        typeAttribute.Value = "Produkt"; //product
                        newDefWorkItemNode.Attributes.Append(typeAttribute);
                        //--------------

                        AddFieldToXmlNode(newDefWorkItemNode, "System.Title", Helper.PreapareStringForTFS(curProduct.Name, Helper.TFS_MaxWorkItemTitleLen));
                        AddFieldToXmlNode(newDefWorkItemNode, "System.Description", Helper.PreapareStringForTFS(curProduct.Description, Helper.TFS_MaxWorkItemTextLen));
                        AddFieldToXmlNode(newDefWorkItemNode, "VMXT.id", curProduct.Id);
                        AddFieldToXmlNode(newDefWorkItemNode, "VMXT.Erzeugung", toolProvider.LanguageDictionary["Creation_Initial"]);
                        AddFieldToXmlNode(newDefWorkItemNode, "VMXT.ProduktTyp", Helper.PreapareStringForTFS(curProduct.TypeName, Helper.TFS_MaxWorkItemTitleLen));

                        defaultWorkItemsNode.AppendChild(newDefWorkItemNode);
                    }
                    catch (Exception e)
                    {
                        toolProvider.ProcessLog.AddEntryAndThrowException(string.Format(Language.Error_Unexpected, e.Message, e.StackTrace));
                    }
                }
            }
            #endregion

            WriteProductTypeDefinitionFile(processProvider.Products, toolProvider.OutputPath, toolProvider.ProcessLog);

            #region Save work items file
            toolProvider.ProcessLog.AddEntry(Language.WorkItems_Saving);
            try
            {
                workItemDoc.Save(workItemTemplateFileName);
            }
            catch
            {
                toolProvider.ProcessLog.AddEntryAndThrowException(string.Format(Language.Error_SavingFailed, workItemTemplateFileName));
            }
            #endregion
        }

        private static void AddFieldToXmlNode(XmlNode node, string fieldName, string fieldValue)
        {
            XmlNode subNodeTitle = node.OwnerDocument.CreateNode("element", "FIELD", "");
            XmlAttribute refnameAttribute = node.OwnerDocument.CreateAttribute("refname");
            refnameAttribute.Value = fieldName;
            subNodeTitle.Attributes.Append(refnameAttribute);
            XmlAttribute valueAttribute = node.OwnerDocument.CreateAttribute("value");
            valueAttribute.Value = fieldValue;
            subNodeTitle.Attributes.Append(valueAttribute);
            node.AppendChild(subNodeTitle);
        }


        private void WriteDecisionGateTypeDefinitionFile(HashSet<string> foundDGTypes, string outputPath, Log log)
        {
            string typeDefinitionFileName = outputPath + "/WorkItem Tracking/TypeDefinitions/VMXT_Entscheidungspunkt.xml";

            #region Load template
            XmlDocument typeDefinitionDoc = new XmlDocument();
            log.AddEntry(Language.DecisionGateTypes_Loading);
            try
            {
                typeDefinitionDoc.Load(typeDefinitionFileName);
            }
            catch
            {
                log.AddEntryAndThrowException(string.Format(Language.Error_LoadingFailed, typeDefinitionFileName));
            }
            #endregion

            log.AddEntry(Language.DecisionGateTypes_SelectingTypesNode);
            var dgTypesNode = typeDefinitionDoc.SelectSingleNode("/WITD/WORKITEMTYPE/FIELDS/FIELD[@refname='VMXT.Entscheidungspunkttyp']/ALLOWEDVALUES");
            if (dgTypesNode == null)
                log.AddEntryAndThrowException(Language.Error_NotFound_InvalidTemplate);

            foreach (string curValue in foundDGTypes)
            {
                log.AddEntry(string.Format(Language.DecisionGateTypes_WritingType, curValue));

                XmlNode newTypeNode = typeDefinitionDoc.CreateNode("element", "LISTITEM", "");
                XmlAttribute valueAttribute = typeDefinitionDoc.CreateAttribute("value");
                valueAttribute.Value = curValue;
                newTypeNode.Attributes.Append(valueAttribute);
                dgTypesNode.AppendChild(newTypeNode);
            }

            #region Save type definition file
            log.AddEntry(Language.DecisionGateTypes_Saving);
            try
            {
                typeDefinitionDoc.Save(typeDefinitionFileName);
            }
            catch
            {
                log.AddEntryAndThrowException(string.Format(Language.Error_SavingFailed, typeDefinitionFileName));
            }
            #endregion
        }

        private void WriteTaskTypeDefinitionFile(List<IDiscipline> disciplineList, string outputPath, Log log)
        {
            string typeDefinitionFileName = outputPath + "/WorkItem Tracking/TypeDefinitions/VMXT_Arbeitsauftrag.xml";

            #region Load template
            XmlDocument typeDefinitionDoc = new XmlDocument();
            log.AddEntry(Language.TaskTypes_Loading);
            try
            {
                typeDefinitionDoc.Load(typeDefinitionFileName);
            }
            catch
            {
                log.AddEntryAndThrowException(string.Format(Language.Groups_Loading, typeDefinitionFileName));
            }
            #endregion

            log.AddEntry(Language.TaskTypes_SelectingDisciplineNodes);
            var disciplinesNode = typeDefinitionDoc.SelectSingleNode("/WITD/WORKITEMTYPE/FIELDS/FIELD[@refname='VMXT.Discipline']/ALLOWEDVALUES");
            if (disciplinesNode == null)
                log.AddEntryAndThrowException(Language.Error_NotFound_InvalidTemplate);

            HashSet<string> alreadyWrittenDisciplines = new HashSet<string>();

            foreach (IDiscipline curValue in disciplineList)
            {
                if (alreadyWrittenDisciplines.Add(curValue.Name))
                {
                    log.AddEntry(string.Format(Language.TaskTypes_WritingDiscipline, curValue.Name));

                    XmlNode newTypeNode = typeDefinitionDoc.CreateNode("element", "LISTITEM", "");
                    XmlAttribute valueAttribute = typeDefinitionDoc.CreateAttribute("value");
                    valueAttribute.Value = curValue.Name;
                    newTypeNode.Attributes.Append(valueAttribute);
                    disciplinesNode.AppendChild(newTypeNode);
                }
            }

            #region Save type definition file
            log.AddEntry(Language.TaskTypes_Saving);
            try
            {
                typeDefinitionDoc.Save(typeDefinitionFileName);
            }
            catch
            {
                log.AddEntryAndThrowException(string.Format(Language.Error_SavingFailed, typeDefinitionFileName));
            }
            #endregion
        }

        private void WriteProductTypeDefinitionFile(List<IProduct> productList, string outputPath, Log log)
        {
            string typeDefinitionFileName = outputPath + "/WorkItem Tracking/TypeDefinitions/VMXT_Produkt.xml";

            #region Load template
            XmlDocument typeDefinitionDoc = new XmlDocument();
            log.AddEntry(Language.ProductTypes_Loading);
            try
            {
                typeDefinitionDoc.Load(typeDefinitionFileName);
            }
            catch
            {
                log.AddEntryAndThrowException(string.Format(Language.Error_LoadingFailed, typeDefinitionFileName));
            }
            #endregion

            log.AddEntry(Language.ProductTypes_SelectingTypesNode);
            var productTypesNode = typeDefinitionDoc.SelectSingleNode("/WITD/WORKITEMTYPE/FIELDS/FIELD[@refname='VMXT.ProduktTyp']/ALLOWEDVALUES");
            if (productTypesNode == null)
                log.AddEntryAndThrowException(Language.Error_NotFound_InvalidTemplate);

            HashSet<string> alreadyWrittenProducts = new HashSet<string>();

            foreach (IProduct curValue in productList)
            {
                if (alreadyWrittenProducts.Add(curValue.Name))
                {
                    log.AddEntry(string.Format(Language.ProductTypes_WritingType, curValue.Name));

                    XmlNode newTypeNode = typeDefinitionDoc.CreateNode("element", "LISTITEM", "");
                    XmlAttribute valueAttribute = typeDefinitionDoc.CreateAttribute("value");
                    valueAttribute.Value = curValue.Name;
                    newTypeNode.Attributes.Append(valueAttribute);
                    productTypesNode.AppendChild(newTypeNode);
                }
            }

            #region Save type definition file
            log.AddEntry(Language.ProductTypes_Saving);
            try
            {
                typeDefinitionDoc.Save(typeDefinitionFileName);
            }
            catch
            {
                log.AddEntryAndThrowException(string.Format(Language.Error_SavingFailed, typeDefinitionFileName));
            }
            #endregion
        }



        public string[] GetIterationPaths()
        {
            return IterationPathList.ToArray();
        }
    }
}
